package net.guruqu.cds;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Vector;

import javax.swing.JFrame;

public class SynchronizedNetworkRunner {
	private Network network;
	private int round;

	public SynchronizedNetworkRunner(Network network) {
		this.network = network;
	}

	public Network getNetwork() {
		return network;
	}

	class TThread extends Thread {
		int startNode, endNode;

		public void run() {
			for (int i = startNode; i < endNode; i++) {
				try {
					network.getNode(i).executeNode();
				} catch (Exception e) {
					e.printStackTrace();
					throw new RuntimeException("Execute Node Failure: " + i);
				}
			}
		}
	}

	public void step(int processor) {
		long last = System.currentTimeMillis();
		Collection<Node> nodes = network.getNodes();
		// exchange message
		for (Node n : nodes) {
			HashMap<Integer, LinkedList<Object>> oom = n
					.getMessageToNeighbour();
			for (Integer i : oom.keySet()) {
				LinkedList<Object> llist = oom.get(i);
				while (!llist.isEmpty()) {
					network.getNode(i).addMessage(n.getUID(),
							llist.removeLast());
				}
			}
		}
		// System.out.println(System.currentTimeMillis() - last);
		// last = System.currentTimeMillis();
		// for (Node n : nodes) {
		// n.executeNode();
		// }

		TThread tt[] = new TThread[processor];
		for (int i = 0; i < tt.length; i++) {
			tt[i] = new TThread();
			tt[i].startNode = i * nodes.size() / processor;
			tt[i].endNode = (i + 1) * nodes.size() / processor;
			tt[i].start();
		}
		for (int i = 0; i < tt.length; i++) {
			try {
				tt[i].join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		getNetwork().globalColourFinished = true;
		int unc = 0;
		for (Node n : getNetwork().getNodes()) {
			if (!((ColorNode) n).colourFinished) {
				getNetwork().globalColourFinished = false;
				unc++;
			}
		}
		System.out.println("Uncolored:" + unc);
		System.out.println("Step simulating takes:"
				+ (System.currentTimeMillis() - last));
		last = System.currentTimeMillis();
	}

	static int find(String s[], String target) {
		for (int i = 0; i < s.length; i++) {
			if (s[i].trim().equalsIgnoreCase(target))
				return i;
		}
		return -1;
	}

	public static void main(String s[]) throws Exception {
		boolean visualize = false;
		{
			if (-1 != find(s, "-v"))
				visualize = true;
		}
		int round = 0;
		Network network = new Network();
		int NODE_SIZE = 300;
		int EDGE_SIZE = (int) (Math.log(NODE_SIZE) * NODE_SIZE) * 5;
		int PROC_SIZE = 2;
		if (-1 != find(s, "-n")) {
			NODE_SIZE = new Integer(s[find(s, "-n") + 1]);
		} else
			throw new RuntimeException("Specify node number by -n first");

		if (-1 != find(s, "-e")) {
			EDGE_SIZE = new Integer(s[find(s, "-e") + 1]);
		} else
			throw new RuntimeException("Specify edge size by -e first");

		if (-1 != find(s, "-p")) {
			PROC_SIZE = new Integer(s[find(s, "-p") + 1]);
		}
		System.out
				.printf("Generating Graph (%d,%d)...\n", NODE_SIZE, EDGE_SIZE);

		Vector<int[]> edges = RandomGraphGenerater.generateEdges(NODE_SIZE,
				EDGE_SIZE);
		System.out.println("Initializing network...");
		network.setGraph(NODE_SIZE, edges);

		SynchronizedNetworkRunner networkRunner = new SynchronizedNetworkRunner(
				network);

		// boolean last[] = new boolean[NODE_SIZE];
		GraphVisualizer graphVisualizer = new GraphVisualizer();
		if (visualize) {
			graphVisualizer.setGraph(NODE_SIZE, edges);
			graphVisualizer.setSize(640, 480);
			graphVisualizer.setNetwork(network);
			graphVisualizer.initialize();
			graphVisualizer.setVisible(true);

			JFrame f = new JFrame("VIsualzier");
			f.getContentPane().add(graphVisualizer);
			f.pack();
			f.setVisible(true);
			f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		}
		
		long start = System.currentTimeMillis();
		while (true) {
			System.out.println("=============Round " + round + " "
					+ network.globalColourFinished);
			networkRunner.step(PROC_SIZE);
			graphVisualizer.rounds = round;
			if (round == 1) {
				int conflict = 0;
				for (Node n : network.getNodes()) {
					ColorNode node = (ColorNode) n;
					for (ColorNode.IntPair i : node.coverEquivalent.keySet()) {
						conflict += node.coverEquivalent.get(i).size();
					}
				}
				// System.out.println("Conflict cover " + conflict);
			}
			int runningNode = 0;
			for (int i = 0; i < NODE_SIZE; i++) {
				boolean bool = ((ColorNode) network.getNode(i)).isConverge();
				if (!bool)
					runningNode++;
			}
			System.out.printf("Node still processing = %d, Color Used=%d\n",
					runningNode, network.maxDeg);
			if (runningNode == 0)
				break;
			Thread.sleep(0);
			round++;
		}
		System.out.printf("All nodes have been processed after %d round\n",
				round);

		int chosen = 0;
		for (Node n : network.getNodes()) {
			// System.out.printf("%d->%s\n", n.getUID(), ((ColorNode) n)
			// .isChosen());
			if ((((ColorNode) n).isChosen()))
				chosen++;
		}
		System.out.printf("Total size of CDS is %d\n", chosen);
		graphVisualizer.active = false;
		
		System.out.printf("Total time used: %d ms",System.currentTimeMillis()-start);
	}

}
